/**
 *******************************************************************************
 * @file      lcd_driver.c
 * @version   V1.0.0    
 * @date      2013.05.20
 * @brief     LCD driver for Tananis.	
 *            Resources: 
 *            TIM5
 * @author    - Adela 
 *            - Robert Zhang <armner@gmail.com>
 *            - 
 *
 */

#include "key_driver.h"
#include "message.h"
#include "stm32f2xx.h"
#include "led_driver.h"
#include "FreeRTOS.h"
#include "semphr.h"


#define GPIO_PIN_KEY_MENU (GPIO_Pin_7)
#define GPIO_PIN_KEY_PAGE    (GPIO_Pin_3)
#define GPIO_PIN_KEY_EXIT    (GPIO_Pin_2)
#define GPIO_PIN_KEY_PLUS   (GPIO_Pin_10)
#define GPIO_PIN_KEY_MINUS    (GPIO_Pin_11)
#define GPIO_PIN_KEY_ENT    (GPIO_Pin_12)

#define KEY_RAW_ENT     (1<<0)
#define KEY_RAW_PLUS    (1<<1)
#define KEY_RAW_MINUS   (1<<2)
#define KEY_RAW_MENU    (1<<3)
#define KEY_RAW_PAGE    (1<<4)
#define KEY_RAW_EXIT     (1<<5)

const int key_table[32] =
{
    KEY_ENTER,
    KEY_PREV,
    KEY_NEXT,
    KEY_NOT_DEFINED,
    KEY_NOT_DEFINED,
    KEY_ESC,
    KEY_NOT_DEFINED
};

#define KEY_BUFFER_SIZE (16)
#define INC_SERIAL_KEY(x) ( (x+1) &(KEY_BUFFER_SIZE-1))
static Key buffer[KEY_BUFFER_SIZE];
static uint8_t wp;
static uint8_t rp;


static xSemaphoreHandle sema_key;

static   int key;
static int read_key_io()
{
    int t_int;
    int t_key =0;
    
    t_int =~GPIOD->IDR;
    if(t_int & (GPIO_PIN_KEY_MENU)) t_key |= KEY_RAW_MENU;
    if(t_int & (GPIO_PIN_KEY_PAGE)) t_key |= KEY_RAW_PAGE;
    if(t_int & (GPIO_PIN_KEY_EXIT)) t_key |= KEY_RAW_EXIT;
    
    t_int =~GPIOE->IDR;
    if(t_int & (GPIO_PIN_KEY_ENT)) t_key |= KEY_RAW_ENT;
    if(t_int & (GPIO_PIN_KEY_PLUS)) t_key |= KEY_RAW_PLUS;
    if(t_int & (GPIO_PIN_KEY_MINUS)) t_key |= KEY_RAW_MINUS;


    return t_key;
}

int key_open(Driver *driver)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    vSemaphoreCreateBinary(sema_key);

    rp=0;
    wp =0;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM5EN, ENABLE);

    GPIOD->BSRRL =GPIO_PIN_KEY_ENT | GPIO_PIN_KEY_MINUS | GPIO_PIN_KEY_PLUS;
    GPIO_InitStructure.GPIO_Pin = GPIO_PIN_KEY_ENT | GPIO_PIN_KEY_MINUS | GPIO_PIN_KEY_PLUS;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
    
    GPIO_Init(GPIOE, &GPIO_InitStructure);
    
    GPIOD->BSRRL =GPIO_PIN_KEY_MENU | GPIO_PIN_KEY_PAGE | GPIO_PIN_KEY_EXIT;
    GPIO_InitStructure.GPIO_Pin = GPIO_PIN_KEY_MENU | GPIO_PIN_KEY_PAGE | GPIO_PIN_KEY_EXIT;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
    
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    read_key_io();
    
    //get system clocks
    RCC_ClocksTypeDef RCC_Clocks;
        
    RCC_GetClocksFreq( &RCC_Clocks);

    /// TIM as key check timer
    //config TIM1 as 9ms pulse driver
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

    /* Time Base configuration */
    TIM_TimeBaseStructure.TIM_Prescaler = (RCC_Clocks.PCLK1_Frequency*2) / 10000 ;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_Period = 500;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);

    TIM_Cmd(TIM5, ENABLE);
    TIM_ITConfig(TIM5, TIM_IT_Update, ENABLE);

    NVIC_SetPriority(TIM5_IRQn, 0xe0);
    NVIC_EnableIRQ(TIM5_IRQn) ;
    return 0;
}



int key_write(Driver *driver, void *buffer, int len, OFFSET offset)
{
    return 0;
}

int key_read(Driver *driver, void *buf, int len, OFFSET offset)
{
    Key *pKey;

    pKey =(Key *)buf;

    while(1)
    {

        if(rp != wp)
        {
            *pKey++ =buffer[rp];
            rp =INC_SERIAL_KEY(rp);

            return sizeof(Key);
        }
        else
        {
            xSemaphoreTake(sema_key, portMAX_DELAY);
        }
    }
}

int key_io_control(Driver *driver, int io_type, int data)
{

    return 0;
}

int key_close(Driver *driver)
{
    return 0;
}

void TIM5_IRQHandler()
{
#if 0
    static char t_char;
    t_char =1;
    led_driver.write(&led_driver, &t_char, 1,0);
#endif
    if(TIM_GetITStatus(TIM5, TIM_IT_Update) )
    {
        int key_changed;
        int prev_key =key;
        int t_bit;
        int i;
        
        TIM_ClearFlag(TIM5, TIM_FLAG_Update);
        key =read_key_io();

        key_changed =key ^prev_key;
        t_bit =1;
        for( i=0; i<32; i++)
        {
            if(key_changed & t_bit)
            {
                if( INC_SERIAL_KEY(wp) !=rp)
                {
                    if(key & t_bit)
                    {
                        buffer[wp].type =MSG_KEY_DOWN;
                    }
                    else{
                        buffer[wp].type =MSG_KEY_UP;
                    }
                    buffer[wp].value =key_table[i];
                    wp =INC_SERIAL_KEY(wp);
                }

                signed portBASE_TYPE xHigherPriorityTaskWoken;
                xHigherPriorityTaskWoken =pdFALSE;
                xSemaphoreGiveFromISR(sema_key, &xHigherPriorityTaskWoken);
            }

            t_bit <<=1;
        }

    }
}

Driver key_driver=
{
    &key_open,
    &key_write,
    &key_read,
    &key_io_control,
    &key_close
};


